package com.winit.schedule.core.conf;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Properties;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.util.CollectionUtils;

import com.winit.schedule.core.utils.StringUtils;

/**
 * 配置管理
 * 
 * @author jianke.zhang 2015-3-11 下午4:30:24
 * @since 1.0
 */
public class Configuration {

    private static final Log           LOG              = LogFactory.getLog(Configuration.class);

    /**
     * List of default Resources. Resources are loaded in the order of the list
     * entries
     */
    private static ArrayList<Resource> defaultResources = new ArrayList<Resource>();

    /**
     * List of default Resources. Resources are loaded in the order of the list
     * entries
     */
    private static ArrayList<Resource> resources        = new ArrayList<Resource>();

    Properties                         properties       = null;

    public Configuration(){

    }

    @SuppressWarnings({ "unchecked", "static-access" })
    public Configuration(Configuration other){
        this.resources = (ArrayList<Resource>) other.resources.clone();
        synchronized (other) {
            if (other.properties != null) {
                this.properties = (Properties) other.properties.clone();
            }
        }
    }

    public static synchronized void addDefaultResource(String path) {
        if (!defaultResources.contains(path)) {
            defaultResources.add(new ClassPathResource(path)); // add to
                                                               // resources
        }
    }

    public static synchronized void addClassPathResource(String path) {
        if (!resources.contains(path)) {
            resources.add(new ClassPathResource(path)); // add to resources
        }
    }

    public String get(String name) {
        if (getProps().containsKey(name)) {
            return getProps().getProperty(name);
        }
        return null;
    }

    public String getTrimmed(String name) {
        String value = get(name);

        if (null == value) {
            return null;
        } else {
            return value.trim();
        }
    }

    public String getString(String name, String defaultValue) {
        String valueString = getTrimmed(name);
        if (valueString == null) return defaultValue;
        return valueString;
    }

    public int getInt(String name, int defaultValue) {
        String valueString = getTrimmed(name);
        if (valueString == null) return defaultValue;
        String hexString = getHexDigits(valueString);
        if (hexString != null) {
            return Integer.parseInt(hexString, 16);
        }
        return Integer.parseInt(valueString);
    }

    public long getLong(String name, long defaultValue) {
        String valueString = getTrimmed(name);
        if (valueString == null) return defaultValue;
        String hexString = getHexDigits(valueString);
        if (hexString != null) {
            return Long.parseLong(hexString, 16);
        }
        return Long.parseLong(valueString);
    }

    public float getFloat(String name, float defaultValue) {
        String valueString = getTrimmed(name);
        if (valueString == null) return defaultValue;
        return Float.parseFloat(valueString);
    }

    public double getDouble(String name, double defaultValue) {
        String valueString = getTrimmed(name);
        if (valueString == null) return defaultValue;
        return Double.parseDouble(valueString);
    }

    public <T extends Enum<T>> T getEnum(String name, T defaultValue) {
        final String val = get(name);
        return null == val ? defaultValue : Enum.valueOf(defaultValue.getDeclaringClass(), val);
    }

    public boolean getBoolean(String name, boolean defaultValue) {
        String valueString = getTrimmed(name);
        if (null == valueString || valueString.isEmpty()) {
            return defaultValue;
        }

        valueString = valueString.toLowerCase();

        if ("true".equals(valueString)) {
            return true;
        } else if ("false".equals(valueString)) {
            return false;
        } else {
            return defaultValue;
        }
    }

    private String getHexDigits(String value) {
        boolean negative = false;
        String str = value;
        String hexString = null;
        if (value.startsWith("-")) {
            negative = true;
            str = value.substring(1);
        }
        if (str.startsWith("0x") || str.startsWith("0X")) {
            hexString = str.substring(2);
            if (negative) {
                hexString = "-" + hexString;
            }
            return hexString;
        }
        return null;
    }

    public Pattern getPattern(String name, Pattern defaultValue) {
        String valString = get(name);
        if (null == valString || valString.isEmpty()) {
            return defaultValue;
        }
        try {
            return Pattern.compile(valString);
        } catch (PatternSyntaxException pse) {
            LOG.warn("Regular expression '" + valString + "' for property '" + name + "' not valid. Using default", pse);
            return defaultValue;
        }
    }

    public Collection<String> getStringCollection(String name) {
        String valueString = get(name);
        return StringUtils.getStringCollection(valueString);
    }

    public String[] getStrings(String name) {
        String valueString = get(name);
        return StringUtils.getStrings(valueString);
    }

    public String[] getStrings(String name, String... defaultValue) {
        String valueString = get(name);
        if (valueString == null) {
            return defaultValue;
        } else {
            return StringUtils.getStrings(valueString);
        }
    }

    public Collection<String> getTrimmedStringCollection(String name) {
        String valueString = get(name);
        if (null == valueString) {
            Collection<String> empty = new ArrayList<String>();
            return empty;
        }
        return StringUtils.getTrimmedStringCollection(valueString);
    }

    public String[] getTrimmedStrings(String name) {
        String valueString = get(name);
        return StringUtils.getTrimmedStrings(valueString);
    }

    public String[] getTrimmedStrings(String name, String... defaultValue) {
        String valueString = get(name);
        if (null == valueString) {
            return defaultValue;
        } else {
            return StringUtils.getTrimmedStrings(valueString);
        }
    }

    /**
     * 获取properties，首次调用会先将配置从文件中加载置properties中
     * 
     * @return
     */
    public synchronized Properties getProps() {
        if (properties == null) {
            properties = new Properties();
            loadResources(properties, defaultResources);
            loadResources(properties, resources);
        }
        return properties;
    }

    /**
     * 加载属性文件
     * 
     * @param properties
     * @param resources
     */
    private void loadResources(Properties properties, ArrayList<Resource> resources) {
        if (null != resources) {
            String fileName = "";
            Properties localProp = null;
            try {
                for (Resource resource : resources) {
                    fileName = resource.getFilename();
                    localProp = PropertiesLoaderUtils.loadProperties(resource);
                    CollectionUtils.mergePropertiesIntoMap(localProp, properties);
                }
            } catch (IOException e) {
                LOG.error("load configuration error. file is :" + fileName, e);
            }
        }
    }

    public void addOrReplaceConfig(Properties localProp) {
        if (null != localProp) {
            Properties properties = getProps();
            CollectionUtils.mergePropertiesIntoMap(localProp, properties);
        }
    }

    /**
     * 重新加载配置信息，讲properties置为空
     */
    public synchronized void reloadConfiguration() {
        properties = null; // trigger reload
    }

    /**
     * Return the number of keys in the configuration.
     * 
     * @return number of keys in the configuration.
     */
    public int size() {
        return getProps().size();
    }

    /**
     * Clears all keys from the configuration.
     */
    public void clear() {
        getProps().clear();
    }

    public void setProps(Properties properties) {
        this.properties = properties;
    }

}
